//////////////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 2014-present, Egret Technology.
//  All rights reserved.
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of the Egret nor the
//       names of its contributors may be used to endorse or promote products
//       derived from this software without specific prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY EGRET AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
//  OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL EGRET AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
//  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;LOSS OF USE, DATA,
//  OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
//  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//////////////////////////////////////////////////////////////////////////////////////

// module eui.sys {

let STATE = "eui.State";
let ADD_ITEMS = "eui.AddItems";
let SET_PROPERTY = "eui.SetProperty";
let SET_STATEPROPERTY = "eui.SetStateProperty";
let BINDING_PROPERTIES = "eui.Binding.$bindProperties";

/**
 * @private
 * 代码生成工具基类
 */
export class CodeBase {

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        return "";
    }

    /**
     * @private
     */
    public indent: number = 0;

    /**
     * @private
     * 获取缩进字符串
     */
    public getIndent(indent?: number): string {
        if (indent === void 0)
            indent = this.indent;
        let str = "";
        for (let i = 0; i < indent; i++) {
            str += "	";
        }
        return str;
    }
}


/**
 * @private
 */
export class EXClass extends CodeBase {

    /**
     * @private
     * 构造函数代码块
     */
    public constructCode: EXCodeBlock;
    /**
     * @private
     * 类名,不包括模块名
     */
    public className: string = "";
    /**
      * @private
      * 全名包括模块名
      */
    public allName: string = "";
    /**
     * @private
     * 父类类名,包括完整模块名
     */
    public superClass: string = "";

    /**
     * @private
     * 内部类区块
     */
    private innerClassBlock: EXClass[] = [];

    /**
     * @private
     * 添加一个内部类
     */
    public addInnerClass(clazz: EXClass): void {
        if (this.innerClassBlock.indexOf(clazz) == -1) {
            this.innerClassBlock.push(clazz);
        }
    }

    /**
     * @private
     * 变量定义区块
     */
    private variableBlock: EXVariable[] = [];

    /**
     * @private
     * 添加变量
     */
    public addVariable(variableItem: EXVariable): void {
        if (this.variableBlock.indexOf(variableItem) == -1) {
            this.variableBlock.push(variableItem);
        }
    }

    /**
     * @private
     * 根据变量名获取变量定义
     */
    public getVariableByName(name: string): EXVariable {
        let list = this.variableBlock;
        let length = list.length;
        for (let i = 0; i < length; i++) {
            let item = list[i];
            if (item.name == name) {
                return item;
            }
        }
        return null;
    }

    /**
     * @private
     * 函数定义区块
     */
    private functionBlock: EXFunction[] = [];

    /**
     * @private
     * 添加函数
     */
    public addFunction(functionItem: EXFunction): void {
        if (this.functionBlock.indexOf(functionItem) == -1) {
            this.functionBlock.push(functionItem);
        }
    }

    /**
     * @private
     * 根据函数名返回函数定义块
     */
    public getFuncByName(name: string): EXFunction {
        let list = this.functionBlock;
        let length = list.length;
        for (let i = 0; i < length; i++) {
            let item = list[i];
            if (item.name == name) {
                return item;
            }
        }
        return null;
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode(isAssignWindow = false): string {

        let indent = this.indent;
        let indentStr = this.getIndent(indent);
        let indent1Str = this.getIndent(indent + 1);
        let indent2Str = this.getIndent(indent + 2);

        //打印类起始块
        let returnStr = indentStr + "(function (";
        if (this.superClass) {
            returnStr += "_super) {\n" + indent1Str + "__extends(" + this.className + ", _super);\n";
        }
        else {
            returnStr += ") {\n";
        }

        //打印内部类列表
        let innerClasses = this.innerClassBlock;
        let length = innerClasses.length;
        for (let i = 0; i < length; i++) {
            let clazz = innerClasses[i];
            clazz.indent = indent + 1;
            if (!isAssignWindow)
                returnStr += indent1Str + "var " + clazz.className + " = " + clazz.toCode() + "\n\n";
            else {
                returnStr += indent1Str + "var " + clazz.className + " = " + clazz.toCode() + "\n";
                returnStr += indent1Str + `window.${clazz.allName}=${clazz.className};\n`;
            }
        }

        returnStr += indent1Str + "function " + this.className + "() {\n";
        if (this.superClass) {
            returnStr += indent2Str + "_super.call(this);\n";
        }

        //打印变量列表
        let variables = this.variableBlock;
        length = variables.length;
        for (let i = 0; i < length; i++) {
            let variable = variables[i];
            if (variable.defaultValue) {
                returnStr += indent2Str + variable.toCode() + "\n";
            }
        }

        //打印构造函数
        if (this.constructCode) {
            let codes = this.constructCode.toCode().split("\n");
            length = codes.length;
            for (let i = 0; i < length; i++) {
                let code = codes[i];
                returnStr += indent2Str + code + "\n";
            }
        }
        returnStr += indent1Str + "}\n";
        returnStr += indent1Str + "var _proto = " + this.className + ".prototype;\n\n";

        //打印函数列表
        let functions = this.functionBlock;
        length = functions.length;
        for (let i = 0; i < length; i++) {
            let functionItem = functions[i];
            functionItem.indent = indent + 1;
            returnStr += functionItem.toCode() + "\n";
        }

        //打印类结尾
        returnStr += indent1Str + "return " + this.className + ";\n" + indentStr;
        if (this.superClass) {
            returnStr += "})(" + this.superClass + ");";
        }
        else {
            returnStr += "})();";
        }
        return returnStr;
    }

}

/**
 * @private
 */
export class EXCodeBlock extends CodeBase {

    /**
     * @private
     * 添加变量声明语句
     * @param name 变量名
     * @param value 变量初始值
     */
    public addVar(name: string, value?: string): void {
        let valueStr = value ? " = " + value : "";
        this.addCodeLine("var " + name + valueStr + ";");
    }

    /**
     * @private
     * 添加赋值语句
     * @param target 要赋值的目标
     * @param value 值
     * @param prop 目标的属性(用“.”访问)，不填则是对目标赋值
     */
    public addAssignment(target: string, value: string, prop?: string): void {
        let propStr = prop ? "." + prop : "";
        this.addCodeLine(target + propStr + " = " + value + ";");
    }

    /**
     * @private
     * 添加返回值语句
     */
    public addReturn(data: string): void {
        this.addCodeLine("return " + data + ";");
    }

    /**
     * @private
     * 添加一条空行
     */
    public addEmptyLine(): void {
        this.addCodeLine("");
    }

    /**
     * @private
     * 开始添加if语句块,自动调用startBlock();
     */
    public startIf(expression: string): void {
        this.addCodeLine("if(" + expression + ")");
        this.startBlock();
    }

    /**
     * @private
     * 开始else语句块,自动调用startBlock();
     */
    public startElse(): void {
        this.addCodeLine("else");
        this.startBlock();
    }

    /**
     * @private
     * 开始else if语句块,自动调用startBlock();
     */
    public startElseIf(expression: string): void {
        this.addCodeLine("else if(" + expression + ")");
        this.startBlock();
    }

    /**
     * @private
     * 添加一个左大括号，开始新的语句块
     */
    public startBlock(): void {
        this.addCodeLine("{");
        this.indent++;
    }

    /**
     * @private
     * 添加一个右大括号,结束当前的语句块
     */
    public endBlock(): void {
        this.indent--;
        this.addCodeLine("}");
    }

    /**
     * @private
     * 添加执行函数语句块
     * @param functionName 要执行的函数名称
     * @param args 函数参数列表
     */
    public doFunction(functionName: string, args: string[]): void {
        let argsStr = args.join(",");
        this.addCodeLine(functionName + "(" + argsStr + ")");
    }

    /**
     * @private
     */
    private lines: string[] = [];

    /**
     * @private
     * 添加一行代码
     */
    public addCodeLine(code: string): void {
        this.lines.push(this.getIndent() + code);
    }

    /**
     * @private
     * 添加一行代码到指定行
     */
    public addCodeLineAt(code: string, index: number): void {
        this.lines.splice(index, 0, this.getIndent() + code);
    }

    /**
     * @private
     * 是否存在某行代码内容
     */
    public containsCodeLine(code: string): boolean {
        return this.lines.indexOf(code) != -1;
    }

    /**
     * @private
     * 在结尾追加另一个代码块的内容
     */
    public concat(cb: EXCodeBlock): void {
        this.lines = this.lines.concat(cb.lines);
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        return this.lines.join("\n");
    }
}

/**
 * @private
 */
export class EXFunction extends CodeBase {

    /**
     * @private
     * 代码块
     */
    public codeBlock: EXCodeBlock = null;

    /**
     * @private
     */
    public isGet: boolean = false;

    /**
     * @private
     * 函数名
     */
    public name: string = "";

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        let indentStr: string = this.getIndent();
        let indent1Str: string = this.getIndent(this.indent + 1);
        let codeIndent: string;
        let returnStr = indentStr;
        if (this.isGet) {
            codeIndent = this.getIndent(this.indent + 2);
            returnStr += 'Object.defineProperty(_proto, "skinParts", {\n';
            returnStr += indent1Str + "get: function () {\n";
        }
        else {
            codeIndent = indent1Str;
            returnStr += "_proto." + this.name + " = function () {\n";
        }

        if (this.codeBlock) {
            let lines = this.codeBlock.toCode().split("\n");
            let length = lines.length;
            for (let i = 0; i < length; i++) {
                let line = lines[i];
                returnStr += codeIndent + line + "\n";
            }
        }
        if (this.isGet) {
            returnStr += indent1Str + "},\n" + indent1Str + "enumerable: true,\n" +
                indent1Str + "configurable: true\n" + indentStr + "});";
        }
        else {
            returnStr += indentStr + "};";
        }

        return returnStr;
    }
}

/**
 * @private
 */
export class EXVariable extends CodeBase {

    /**
     * @private
     */
    public constructor(name: string, defaultValue?: string) {
        super();
        this.indent = 2;
        this.name = name;
        this.defaultValue = defaultValue;
    }

    /**
     * @private
     * 变量名
     */
    public name: string;
    /**
     * @private
     * 默认值
     */
    public defaultValue: string;

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        if (!this.defaultValue) {
            return "";
        }
        return "this." + this.name + " = " + this.defaultValue + ";";
    }
}

export class EXArray extends CodeBase {

    constructor(private array: string[]) {
        super();
    }

    toCode() {
        return "[" + this.array.map(v => "\"" + v + "\"").join(",") + "]"
    }
}


/**
 * @private
 */
export class EXState extends CodeBase {

    /**
     * @private
     */
    public constructor(name: string, stateGroups?: Array<any>) {
        super();
        this.name = name;
        if (stateGroups)
            this.stateGroups = stateGroups;
    }

    /**
     * @private
     * 视图状态名称
     */
    public name: string = "";

    /**
     * @private
     */
    public stateGroups: Array<any> = [];

    /**
     * @private
     */
    public addItems: Array<any> = [];

    /**
     * @private
     */
    public setProperty: Array<any> = [];

    /**
     * @private
     * 添加一个覆盖
     */
    public addOverride(item: CodeBase): void {
        if (item instanceof EXAddItems)
            this.addItems.push(item);
        else
            this.setProperty.push(item);
    }

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        let indentStr: string = this.getIndent(1);
        let returnStr: string = "new " + STATE + " (\"" + this.name + "\",\n" + indentStr + "[\n";
        let index: number = 0;
        let isFirst: boolean = true;
        let overrides: Array<any> = this.addItems.concat(this.setProperty);
        while (index < overrides.length) {
            if (isFirst)
                isFirst = false;
            else
                returnStr += ",\n";
            let item: CodeBase = overrides[index];
            let codes: Array<any> = item.toCode().split("\n");
            let length: number = codes.length;
            for (let i: number = 0; i < length; i++) {
                let code: string = codes[i];
                codes[i] = indentStr + indentStr + code;
            }
            returnStr += codes.join("\n");
            index++;
        }
        returnStr += "\n" + indentStr + "])";
        return returnStr;
    }
}

/**
 * @private
 */
export class EXAddItems extends CodeBase {
    /**
     * @private
     */
    public constructor(target: string, property: string, position: number, relativeTo: string) {
        super();
        this.target = target;
        this.property = property;
        this.position = position;
        this.relativeTo = relativeTo;
    }

    /**
     * @private
     * 要添加的实例
     */
    public target: string;

    /**
     * @private
     * 要添加到的属性
     */
    public property: string;

    /**
     * @private
     * 添加的位置
     */
    public position: number;

    /**
     * @private
     * 相对的显示元素
     */
    public relativeTo: string;

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        let returnStr: string = "new " + ADD_ITEMS + "(\"" + this.target + "\",\"" + this.property + "\"," + this.position + ",\"" + this.relativeTo + "\")";
        return returnStr;
    }
}

/**
 * @private
 */
export class EXSetProperty extends CodeBase {
    /**
     * @private
     */
    public constructor(target: string, name: string, value: string) {
        super();
        this.target = target;
        this.name = name;
        this.value = value;
    }

    /**
     * @private
     * 要修改的属性名
     */
    public name: string;

    /**
     * @private
     * 目标实例名
     */
    public target: string;

    /**
     * @private
     * 属性值
     */
    public value: string;

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        return "new " + SET_PROPERTY + "(\"" + this.target + "\",\"" + this.name + "\"," + this.value + ")";
    }
}
/**
 * @private
 */
export class EXSetStateProperty extends CodeBase {
    /**
     * @private
     */
    public constructor(target: string, property: string, templates: string[], chainIndex: number[]) {
        super();
        if (target) {
            target = "this." + target;
        } else {
            target = "this";
        }
        this.target = target;
        this.property = property;
        this.templates = templates;
        this.chainIndex = chainIndex;
    }
    /**
     * @private
     * 目标实例名
     */
    public target: string;
    /**
     * @private
     * 目标属性名
     */
    public property: string;

    /**
     * @private
     * 绑定的模板列表
     */
    public templates: string[];

    /**
     * @private
     * chainIndex是一个索引列表，每个索引指向templates中的一个值，该值是代表属性链。
     */
    public chainIndex: number[];

    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        let expression = this.templates.join(",");
        let chain = this.chainIndex.join(",");
        return "new " + SET_STATEPROPERTY + "(this, [" + expression + "]," + "[" + chain + "]," +
            this.target + ",\"" + this.property + "\")";

    }
}
/**
 * @private
 */
export class EXBinding extends CodeBase {

    /**
     * @private
     */
    public constructor(target: string, property: string, templates: string[], chainIndex: number[]) {
        super();
        this.target = target;
        this.property = property;
        this.templates = templates;
        this.chainIndex = chainIndex;
    }

    /**
     * @private
     * 目标实例名
     */
    public target: string;
    /**
     * @private
     * 目标属性名
     */
    public property: string;
    /**
     * @private
     * 绑定的模板列表
     */
    public templates: string[];

    /**
     * @private
     * chainIndex是一个索引列表，每个索引指向templates中的一个值，该值是代表属性链。
     */
    public chainIndex: number[];


    /**
     * @private
     *
     * @returns
     */
    public toCode(): string {
        let expression = this.templates.join(",");
        let chain = this.chainIndex.join(",");
        return BINDING_PROPERTIES + "(this, [" + expression + "]," + "[" + chain + "]," +
            this.target + ",\"" + this.property + "\");";

    }
}
// }